package com.boot.aop;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.boot.dto.OperationLogDTO;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;

@Aspect
@Component
public class OperationLogAspect {

    private static final Logger log = LoggerFactory.getLogger("OPERATION_LOG");

    @Pointcut("execution(* com.boot.controller.*.*(..))")
    public void operationLog() {
    }

    @Around("operationLog()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (attributes == null) return joinPoint.proceed();

        HttpServletRequest request = attributes.getRequest();
        OperationLogDTO logDTO = new OperationLogDTO();
        long startTime = System.currentTimeMillis();

        // 设置基本信息
        logDTO.setMethod(request.getMethod());
        logDTO.setUri(request.getRequestURI());
        logDTO.setIp(getClientIP(request));
        logDTO.setTimestamp(java.time.LocalDateTime.now());

        // 获取请求参数（Query + Body）
        Map<String, Object> params = new HashMap<>();
        Map<String, String[]> parameterMap = request.getParameterMap();
        if (!parameterMap.isEmpty()) {
            parameterMap.forEach((key, value) -> params.put(key, value.length == 1 ? value[0] : value));
        }

        // 尝试获取 JSON Body（注意：只能读一次，需配合 requestBodyAdvice 处理重复读）
        Object requestBody = getRequestBody(joinPoint);
        if (requestBody != null) {
            params.put("body", requestBody);
        }
        logDTO.setRequestParams(params);

        // 执行方法
        Object result;
        try {
            result = joinPoint.proceed();
            logDTO.setStatus(200);
            logDTO.setResponseBody("");
        } catch (Exception e) {
            logDTO.setStatus(500);
            logDTO.setResponseBody(e.getMessage());
            throw e;
        } finally {
            logDTO.setTakeTime(System.currentTimeMillis() - startTime);
            log.info(JSONUtil.toJsonStr(logDTO)); // 输出为 JSON 格式日志
        }

        return result;
    }

    private String getClientIP(HttpServletRequest request) {
        String xForwardedFor = request.getHeader("X-Forwarded-For");
        if (StrUtil.isNotBlank(xForwardedFor) && !"unknown".equalsIgnoreCase(xForwardedFor)) {
            return xForwardedFor.split(",")[0].trim();
        }
        String xRealIP = request.getHeader("X-Real-IP");
        if (StrUtil.isNotBlank(xRealIP) && !"unknown".equalsIgnoreCase(xRealIP)) {
            return xRealIP;
        }
        return request.getRemoteAddr();
    }

    private Object getRequestBody(ProceedingJoinPoint joinPoint) {
        for (Object arg : joinPoint.getArgs()) {
            if (arg != null && !arg.getClass().isPrimitive() && !arg.getClass().getName().startsWith("java.") && !(arg instanceof MultipartFile)) {
                try {
                    // 使用 Hutool 提供的深拷贝 + 脱敏
                    return deepCopyAndMaskPassword(arg);
                } catch (Exception e) {
                    return "[无法序列化请求体]";
                }
            }
        }
        return null;
    }

    /**
     * 深度复制并屏蔽 password 字段
     */
    private Object deepCopyAndMaskPassword(Object obj) {
        try {
            // 使用 JSON 序列化再反序列化实现深拷贝
            String json = JSONUtil.toJsonStr(obj);
            Object parsed = JSONUtil.parseObj(json); // 得到 JSONObject 或 JSONArray

            maskPasswordInObject(parsed);
            return parsed;
        } catch (Exception e) {
            return obj; // 出错则原样返回（但可能泄露）
        }
    }

    /**
     * 递归遍历对象结构，将所有名为 'password'/'passwd'/'pwd' 的字段设为 ***
     */
    private void maskPasswordInObject(Object obj) {
        if (obj == null) {
            return;
        }

        // 判断是否为 Hutool 的 JSONObject
        if (obj instanceof cn.hutool.json.JSONObject) {
            cn.hutool.json.JSONObject jsonObject = (cn.hutool.json.JSONObject) obj;
            for (String key : jsonObject.keySet()) {
                Object value = jsonObject.get(key);
                if (isPasswordField(key)) {
                    jsonObject.set(key, "***");
                } else {
                    maskPasswordInObject(value);
                }
            }
        }
        // 判断是否为 Hutool 的 JSONArray
        else if (obj instanceof cn.hutool.json.JSONArray) {
            cn.hutool.json.JSONArray jsonArray = (cn.hutool.json.JSONArray) obj;
            for (Object item : jsonArray) {
                maskPasswordInObject(item);
            }
        }
    }

    private boolean isPasswordField(String key) {
        key = key.toLowerCase();
        return "password".equals(key) || "passwd".equals(key) || "pwd".equals(key);
    }
}
